package com.eastfair.venuebooking.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
//import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.eastfair.auth.api.UserAccountServiceFeign;
import com.eastfair.boot.request.PageParams;
import com.eastfair.boot.service.SuperServiceImpl;
import com.eastfair.common.dto.OrderPaymentPlan;
import com.eastfair.constant.BusinessConstant;
import com.eastfair.constant.BusinessInnerMsgTemplate;
import com.eastfair.core.base.R;
import com.eastfair.core.base.entity.SuperEntity;
import com.eastfair.core.context.ContextUtil;
import com.eastfair.core.context.StaticContextUtil;
import com.eastfair.core.exception.BizException;
import com.eastfair.core.utils.WordUtil;
import com.eastfair.file.api.FileServiceFeign;
import com.eastfair.file.vo.FileVO;
import com.eastfair.messagecenter.util.SendMsgUtil;
import com.eastfair.pay.dto.InvoiceCallbackDTO;
import com.eastfair.pay.dto.TransactionOrderDTO;
import com.eastfair.pay.enumeration.TransactionOrderOrderStatusEnum;
import com.eastfair.pay.enumeration.TransactionOrderOrderTypeEnum;
import com.eastfair.pay.enums.PayStateEnum;
import com.eastfair.pay.vo.ContractFinanceVO;
import com.eastfair.pay.vo.PaymentPlanVO;
import com.eastfair.projectcore.enumeration.MenuAffiliationEnum;
import com.eastfair.util.OrderUtil;
import com.eastfair.venuebooking.dao.ContractManageMapper;
import com.eastfair.venuebooking.dto.*;
import com.eastfair.venuebooking.entity.*;
import com.eastfair.venuebooking.enumeration.ContractManageContractStatusEnum;
import com.eastfair.venuebooking.enumeration.ExhibitionStateEnum;
import com.eastfair.venuebooking.exceptioncode.VenueBookingExceptionCode;
import com.eastfair.venuebooking.service.*;
import com.eastfair.venuebooking.vo.*;
import fr.opensagres.xdocreport.core.XDocReportException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.ContentType;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 业务实现类
 * 合同
 * </p>
 *
 * @author lyw
 * @date 2022-06-20
 */
@Slf4j
@Service
//@DS("#thread.tenant")
public class ContractManageServiceImpl extends SuperServiceImpl<ContractManageMapper, ContractManage> implements ContractManageService {

    @Value("${pay.payee.venue-booking}")
    private String venueBookingPayee;

    @Resource
    private ContractManageMapper contractManageMapper;

    @Resource
    private VenueBookingContractService venueBookingContractService;

    @Resource
    private ExhibitionService exhibitionService;

    @Resource
    private CustomerMainService customerMainService;

    @Resource
    private QuotationService quotationService;

    @Resource
    private TemplateManageService templateManageService;

    @Resource
    private UserAccountServiceFeign userAccountServiceFeign;

    @Resource
    private FileServiceFeign fileServiceFeign;

    @Resource
    private VenueBookingPaymentPlanService venueBookingPaymentPlanService;

    @Resource
    private VenueBookingTransactionOrderService venueBookingTransactionOrderService;

    @Resource
    private SendMsgUtil sendMsgUtil;

    @Resource(name = "threadPoolTaskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    private static final String CONTRACT_NUMBER_PREFIX = "CN";

    /**
     * 处理逻辑删除相关处理
     *
     * @param modelList 实体
     * @return 是否成功
     */
    @Override
    protected R<Boolean> handlerRemoveByIdsOfLogic(Collection<ContractManage> modelList) {
        modelList.forEach(s -> s.setIsDeleted(BusinessConstant.YES));
        return R.successDef();
    }

    @Override
    protected R<Boolean> handlerSave(ContractManage model) {
        model.setProjectId(ContextUtil.getProjectId());
        model.setSubsystemId(ContextUtil.getSubSystemId());
        if (model.getContractStatus() == null) {
            model.setContractStatus(ContractManageContractStatusEnum.SUBMIT);
        }
        return R.successDef();
    }

    @Override
    public IPage<ContractManageVO> listContractManageVOs(PageParams<ContractManageQuery> pageParams) {
        log.info("listContractManageVOs - 查询合同列表, pageParams={}", pageParams);

        Page<ContractManage> page = pageParams.buildPage();
        ContractManageQuery query = pageParams.getModel();
        QueryWrapper<ContractManage> queryWrapper = createContractManageQueryWrapper(query);
        IPage<ContractManageVO> voPage = contractManageMapper.listContractManageVOs(page, queryWrapper);

        List<ContractManageVO> records = voPage.getRecords();
        if (records == null || records.isEmpty()) {
            return voPage;
        }
        // 计算可开票金额
        records.forEach(contractManageVO -> {
            BigDecimal invoicedAmount = contractManageVO.getInvoicedAmount();
            BigDecimal sureInvoicedAmount = contractManageVO.getContractPayState().equals(PayStateEnum.UN_PAY.getCode()) ? new BigDecimal(0) : contractManageVO.getContractReturndAmount()
                    .subtract(invoicedAmount);
            contractManageVO.setSureInvoicedAmount(sureInvoicedAmount);
        });
        return voPage;
    }

    @Override
    public ContractManageVO getContractManageVOById(Long id) {
        log.info("getContractManageVOById - id查询详情, id={}", id);
        ContractManage contractManage = getById(id);
        if (contractManage == null) {
            throw BizException.wrap(VenueBookingExceptionCode.CONTRACT_DOES_NOT_EXIST);
        }
        ContractManageVO contractManageVO = BeanUtil.toBean(contractManage, ContractManageVO.class);
        // 创建人/更新人 信息
        R<String> createdByNameR = userAccountServiceFeign.getName(contractManageVO.getCreatedBy());
        contractManageVO.setCreatedByName(createdByNameR.getData());
        R<String> updatedByNameR = userAccountServiceFeign.getName(contractManageVO.getUpdatedBy());
        contractManageVO.setUpdatedByName(updatedByNameR.getData());
        // 查询展会
        Exhibition exhibition = exhibitionService.getById(contractManage.getExhibitionId());
        if (exhibition != null) {
            contractManageVO.setExhibitionName(exhibition.getExhibitionName());
        }
        // 查询客户
        CustomerMain customerMain = customerMainService.getById(contractManage.getCutomerId());
        if (customerMain != null) {
            contractManageVO.setCutomerName(customerMain.getCustomerCnName());
        }
        // 查询报价书
        Quotation quotation = quotationService.getById(contractManage.getQuotationId());
        if (quotation != null) {
            contractManageVO.setQuotationTitle(quotation.getQuotationTitle());
        }
        // 查询合同模版
        TemplateManage templateManage = templateManageService.getById(contractManage.getContractTemplateId());
        if (templateManage != null) {
            contractManageVO.setContractTemplateName(templateManage.getTemName());
        }
        // 查询付款信息
        ContractFinanceVO contractFinanceVO = venueBookingContractService.queryFinance(id);
        if (contractFinanceVO != null) {
            PayApiContractFinanceVO payApiContractFinanceVO = BeanUtil.toBean(contractFinanceVO, PayApiContractFinanceVO.class);
            if (contractFinanceVO.getLastPayTime() != null) {
                DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                String format = dateTimeFormatter.format(contractFinanceVO.getLastPayTime());
                payApiContractFinanceVO.setLastPayTime(format);
            }
            contractManageVO.setContractFinance(payApiContractFinanceVO);
        }
        return contractManageVO;
    }

    @Override
    public ContractManageVO getVOByContractNumber(String contractNumber) {
        log.info("getVOByContractNumber - 合同编号获取合同VO, contractNumber={}", contractNumber);
        ContractManage contractManage = getByContractNumber(contractNumber);
        if (contractManage == null) {
            return null;
        }
        ContractManageVO contractManageVO = BeanUtil.toBean(contractManage, ContractManageVO.class);
        QuotationVO quotationVO = quotationService.getQuotationVOById(contractManage.getQuotationId());
        if (quotationVO == null) {
            return contractManageVO;
        }
        Integer spaceNum = quotationVO.getSpaceNum();
        contractManageVO.setSpaceNum(spaceNum);
        return contractManageVO;
    }

    @Override
    public ContractManage getByContractNumber(String contractNumber) {
        ContractManageQuery query = new ContractManageQuery();
        query.setContractNumber(contractNumber);
        List<ContractManage> list = listContractManage(query);
        if (list == null || list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

    @Override
    public List<ContractManage> listContractManage(ContractManageQuery query) {
        log.info("listContractManage - 查询合同列表, contractManageQuery={}", query);
        QueryWrapper<ContractManage> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .lambda()
                .eq(ContractManage::getIsDeleted, BusinessConstant.DELETE_NO)
                .eq(ContractManage::getIsEnabled, BusinessConstant.ENABLE_YES)
                .eq(StrUtil.isNotBlank(query.getContractNumber()), ContractManage::getContractNumber, query.getContractNumber())
                .eq(query.getCutomerId() != null, ContractManage::getCutomerId, query.getCutomerId())
                .eq(query.getExhibitionId() != null, ContractManage::getExhibitionId, query.getExhibitionId())
                .in(query.getIdList() != null && !query.getIdList().isEmpty(), ContractManage::getId, query.getIdList())
        ;
        return list(queryWrapper);
    }

    @Override
    public List<ContractManage> listContractManageByCustomerId(Long customerId) {
        ContractManageQuery contractManageQuery = new ContractManageQuery();
        contractManageQuery.setCutomerId(customerId);
        return listContractManage(contractManageQuery);
    }

    @Override
    public List<ContractManage> listContractManageByExhibitionId(Long exhibitionId) {
        ContractManageQuery contractManageQuery = new ContractManageQuery();
        contractManageQuery.setExhibitionId(exhibitionId);
        return listContractManage(contractManageQuery);
    }

    @Override
    public List<ContractManageVO> listVOByIds(List<Long> ids) {
        ContractManageQuery contractManageQuery = new ContractManageQuery();
        contractManageQuery.setIdList(ids);
        List<ContractManage> list = listContractManage(contractManageQuery);
        if (list == null || list.isEmpty()) {
            return Collections.emptyList();
        }
        return list
                .stream()
                .map(contractManage -> BeanUtil.toBean(contractManage, ContractManageVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public List<ContractManageVO> listByPlanPayTime(String startPlanPayTime, String endPlanPayTime) {
        log.info("listByPlanPayTime - 计划付款时间范围查询未付款的合同, startPlanPayTime={}, endPlanPayTime={}", startPlanPayTime, endPlanPayTime);
        // 查询付款计划
        List<PaymentPlanVO> paymentPlanList = venueBookingPaymentPlanService.queryByPlanPayTime(startPlanPayTime, endPlanPayTime);
        if (paymentPlanList == null || paymentPlanList.isEmpty()) {
            return Collections.emptyList();
        }
        // 通过付款计划列表找到合同ID列表
        Map<Long, List<PaymentPlanVO>> paymentPlanMap = paymentPlanList
                .stream()
                .collect(Collectors.groupingBy(PaymentPlanVO::getBusinessId));
        List<Long> contractManageIdList = new ArrayList<>(paymentPlanMap.keySet());
        // 合同ID列表查询合同列表
        List<ContractManageVO> contractManageList = listVOByIds(contractManageIdList);
        if (contractManageList == null || contractManageList.isEmpty()) {
            return Collections.emptyList();
        }
        // 合同中封装对应的付款计划
        for (ContractManageVO contractManageVO: contractManageList) {
            List<PaymentPlanVO> contractPaymentPlanList = paymentPlanMap.get(contractManageVO.getId());
            if (contractPaymentPlanList == null || contractPaymentPlanList.isEmpty()) {
                continue;
            }
            List<PayApiPaymentPlanVO> payApiPaymentPlanList = contractPaymentPlanList
                    .stream()
                    .map(paymentPlanVO -> BeanUtil.toBean(paymentPlanVO, PayApiPaymentPlanVO.class))
                    .collect(Collectors.toList());
            contractManageVO.setPaymentPlanList(payApiPaymentPlanList);
        }
        return contractManageList;
    }

    @Override
    public ContractManageVO countContractManageByCustomerId(Long customerId) {
        List<ContractManage> list = listContractManageByCustomerId(customerId);
        BigDecimal totalContractAmount = BigDecimal.ZERO;
        BigDecimal totalContractReturndAmount = BigDecimal.ZERO;
        BigDecimal totalInvoicedAmount = BigDecimal.ZERO;
        BigDecimal totalSureInvoicedAmount = BigDecimal.ZERO;
        if (list != null && !list.isEmpty()) {
            for (ContractManage contractManage : list) {
                BigDecimal contractAmount = contractManage.getContractAmount();
                if (contractAmount != null) {
                    totalContractAmount = totalContractAmount.add(contractAmount);
                }
                BigDecimal contractReturndAmount = contractManage.getContractReturndAmount();
                if (contractReturndAmount != null) {
                    totalContractReturndAmount = totalContractReturndAmount.add(contractReturndAmount);
                }
                BigDecimal invoicedAmount = contractManage.getInvoicedAmount();
                if (invoicedAmount != null) {
                    totalInvoicedAmount = totalSureInvoicedAmount.add(invoicedAmount);
                }
            }
        }
        ContractManageVO contractManageVO = new ContractManageVO();
        contractManageVO.setContractAmount(totalContractAmount);
        contractManageVO.setContractReturndAmount(totalContractReturndAmount);
        contractManageVO.setInvoicedAmount(totalInvoicedAmount);
        // 计算未开票金额
        totalSureInvoicedAmount = totalContractReturndAmount
                .subtract(totalInvoicedAmount)
                .setScale(2, RoundingMode.HALF_UP);
        contractManageVO.setSureInvoicedAmount(totalSureInvoicedAmount);
        return contractManageVO;
    }

    @Override
    public List<ContractHistogramVO> countEveryYearContractAmountByCustomerId(Long id) {
        log.info("countEveryYearContractAmountByCustomerId - 客户ID统计每年的成交量, id={}", id);

        return contractManageMapper.countEveryYearContractAmountByCustomerId(id);
    }

    @Override
    public boolean existContractManage(ContractManageQuery query) {
        log.info("existContractManage - 合同是否存在, query={}", query);
        QueryWrapper<ContractManage> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(ContractManage::getIsDeleted, BusinessConstant.DELETE_NO)
                .in(query.getCustomerIdList() != null && !query.getCustomerIdList().isEmpty(),
                        ContractManage::getCutomerId, query.getCustomerIdList())
                .in(query.getQuotationIdList() != null && !query.getQuotationIdList().isEmpty(),
                        ContractManage::getQuotationId, query.getQuotationIdList())
        ;
        return count(queryWrapper) > 0;
    }

    @Override
    public boolean existContractManageByCustomerIds(List<Long> customerId) {
        ContractManageQuery query = new ContractManageQuery();
        query.setCustomerIdList(customerId);
        return existContractManage(query);
    }

    @Override
    public boolean existContractManageByQuotationIds(List<Long> quotationIds) {
        ContractManageQuery query = new ContractManageQuery();
        query.setQuotationIdList(quotationIds);
        return existContractManage(query);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveContract(ContractManageDTO contractManageDTO){
        log.info("saveContract - 保存合同, saveDTO={}", contractManageDTO);
        List<Long> quotationIds = new ArrayList<>();
        quotationIds.add(contractManageDTO.getQuotationId());
        if (existContractManageByCustomerIds(quotationIds)) {
            throw BizException.wrap(VenueBookingExceptionCode.QUOTATION_EXIST_CONTRACT);
        }
        // 生成合同编号
        contractManageDTO.setContractNumber(OrderUtil.getOrderNo(CONTRACT_NUMBER_PREFIX));
        contractManageDTO.setSpaceAmount(contractManageDTO.getContractAmount().subtract(contractManageDTO.getSpaceBond()).subtract(contractManageDTO.getServiceBond()));
        ContractManage contractManage = BeanUtil.toBean(contractManageDTO, ContractManage.class);
        contractManage.setContractPayState(PayStateEnum.UN_PAY.getCode());
        // 模版套打，生成合同文件
        processContractTemplate(contractManage);
        // 保存合同
        save(contractManage);
        // 展会状态修改
        if (Objects.nonNull(contractManageDTO.getExhibitionId())) {
            exhibitionService.updateExhibitionState(contractManageDTO.getExhibitionId(), ExhibitionStateEnum.STATE_FIVE);
        }
        // 支付系统同步合同
        contractManageDTO.setId(contractManage.getId());
        venueBookingContractService.saveOrUpdate(contractManageDTO);
        // 保存支付系统交易订单
        saveTransactionOrder(contractManage.getContractNumber());
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateContract(ContractManageDTO contractManageDTO) {
        log.info("updateContract - 修改合同, updateDTO={}", contractManageDTO);

        ContractManage rawContract = getById(contractManageDTO.getId());
        List<Long> quotationIds = new ArrayList<>();
        quotationIds.add(contractManageDTO.getQuotationId());
        if (!rawContract.getQuotationId().equals(contractManageDTO.getQuotationId()) && existContractManageByCustomerIds(quotationIds)) {
            throw BizException.wrap(VenueBookingExceptionCode.QUOTATION_EXIST_CONTRACT);
        }
        BeanUtil.copyProperties(contractManageDTO, rawContract);
        // 生成合同文件
        processContractTemplate(rawContract);
        // 更新合同
        updateById(rawContract);
        // 更新支付系统的合同
        venueBookingContractService.saveOrUpdate(contractManageDTO);
        // 更新支付系统订单
        updateTransactionOrder(contractManageDTO);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateContractDisableByIds(List<Long> ids) {
        log.info("updateContractDisableByIds - 批量ID更新合同为失效, ids={}", ids);
        for (Long id : ids) {
            if (!venueBookingContractService.isCollected(id)) {
                throw BizException.wrap(VenueBookingExceptionCode.CONTRACT_HAS_COLLECT);
            }
        }
        // 更新合同为失效
        List<ContractManage> list = listByIds(ids);
        list.forEach(contractManage -> {
            if (ContractManageContractStatusEnum.APPROVED.eq(contractManage.getContractStatus())) {
                throw BizException.wrap(VenueBookingExceptionCode.CONTRACT_CAN_NOT_CANCEL.getCode(), "合同已确认，不可作废");
            }
            contractManage.setIsEnabled(BusinessConstant.ENABLE_NO);
            contractManage.setContractStatus(ContractManageContractStatusEnum.CANCEL);
        });
        updateBatchById(list);
        // 删除支付系统合同
        for (Long id : ids) {
            venueBookingContractService.delete(id);
        }
        // 取消支付系统订单
        for (ContractManage contractManage: list) {
            venueBookingTransactionOrderService.cancel(contractManage.getContractNumber());
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateContractDisableByExhibitionId(Long exhibitionId) {
        List<ContractManage> contractManages = listContractManageByExhibitionId(exhibitionId);
        if (contractManages == null || contractManages.isEmpty()) {
            return false;
        }
        // 更新合同为失效
        List<Long> ids = contractManages
                .stream()
                .map(SuperEntity::getId)
                .collect(Collectors.toList());
        return updateContractDisableByIds(ids);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateContractAndQuotationDisableByExhibitionId(Long exhibitionId) {
        // 合同是否可以清空，如果有已经认款，则不可清空
        List<ContractManage> contractManages = listContractManageByExhibitionId(exhibitionId);
        boolean canDisableContract = true;
        for (ContractManage contractManage : contractManages) {
            if (contractManage.getContractPayState() != null && !PayStateEnum.UN_PAY.getCode().equals(contractManage.getContractPayState())) {
                canDisableContract = false;
                break;
            }
        }
        if (!canDisableContract) {
            throw BizException.wrap(VenueBookingExceptionCode.EX_CANT_NOT_CLEAR_CONTRACT);
        }
        updateContractDisableByExhibitionId(exhibitionId);
        quotationService.updateQuotationDisableByExhibitionId(exhibitionId);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateContractForCollect(ContractManageDTO contractManageDTO) {
        log.info("updateContractForPay - 支付之后更新合同信息, contractManageDTO={}", contractManageDTO);
        UpdateWrapper<ContractManage> updateWrapper = new UpdateWrapper<>();
        updateWrapper
                .lambda()
                .eq(contractManageDTO.getId() != null, ContractManage::getId, contractManageDTO.getId())
                .set(contractManageDTO.getContractPayState() != null,
                        ContractManage::getContractPayState, contractManageDTO.getContractPayState())
                .set(contractManageDTO.getContractReturndAmount() != null,
                        ContractManage::getContractReturndAmount, contractManageDTO.getContractReturndAmount());
        boolean b = update(updateWrapper);
        ContractManage contractManage = this.getById(contractManageDTO.getId());
        // 处理展会状态
        if (Objects.nonNull(contractManage) && Objects.nonNull(contractManage.getExhibitionId())) {
            exhibitionService.dealPaidResult(contractManage.getExhibitionId());
        }
        return b;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateContractStatusApproved(Long id) {
        log.info("updateContractApproved - 确认合同, id={}", id);
        ContractManage contractManage = getById(id);
        if (contractManage == null) {
            throw BizException.wrap(VenueBookingExceptionCode.CONTRACT_DOES_NOT_EXIST);
        }
        if (ContractManageContractStatusEnum.APPROVED.eq(contractManage.getContractStatus())) {
            return true;
        }
        // 更新合同状态为已审核
        contractManage.setContractStatus(ContractManageContractStatusEnum.APPROVED);
        return updateById(contractManage);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateBelongTo(ContractManageAssignDTO assignDTO) {
        log.info("updateBelongTo - 更新归属, assignDTO={}", assignDTO);
        ContractManage contractManage = getById(assignDTO.getId());
        if (contractManage == null) {
            throw BizException.wrap(VenueBookingExceptionCode.CONTRACT_DOES_NOT_EXIST);
        }
        ContractManageDTO updateDTO = new ContractManageDTO();
        updateDTO.setId(assignDTO.getId());
        updateDTO.setBelongTo(assignDTO.getBelongTo());
        updateDTO.setBelongToName(assignDTO.getBelongToName());
        updateDTO.setOrgCode(assignDTO.getOrgCode());
        updateDTO.setOrgName(assignDTO.getOrgName());
        updateDTO.setLastBelongTo(contractManage.getBelongTo());
        updateDTO.setLastBelongToName(contractManage.getBelongToName());
        updateDTO.setLastOrgCode(contractManage.getOrgCode());
        updateDTO.setLastOrgName(contractManage.getOrgName());
        return updateBelongTo(updateDTO);
    }

    @Override
    public boolean updateBelongTo(ContractManageDTO updateDTO) {
        UpdateWrapper<ContractManage> updateWrapper = new UpdateWrapper<>();
        updateWrapper
                .lambda()
                .set(ContractManage::getLastBelongTo, updateDTO.getLastBelongTo())
                .set(ContractManage::getLastBelongToName, updateDTO.getLastBelongToName())
                .set(ContractManage::getLastOrgCode, updateDTO.getLastOrgCode())
                .set(ContractManage::getLastOrgName, updateDTO.getLastOrgName())
                .set(ContractManage::getBelongTo, updateDTO.getBelongTo())
                .set(ContractManage::getBelongToName, updateDTO.getBelongToName())
                .set(ContractManage::getOrgCode, updateDTO.getOrgCode())
                .set(ContractManage::getOrgName, updateDTO.getOrgName())
                .eq(updateDTO.getId() != null, ContractManage::getId, updateDTO.getId());
        return update(updateWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateFollow(ContractManageDTO updateDTO) {
        UpdateWrapper<ContractManage> updateWrapper = new UpdateWrapper<>();
        updateWrapper
                .lambda()
                .eq(updateDTO.getId() != null, ContractManage::getId, updateDTO.getId())
                .set(updateDTO.getLastFollowDate() != null,
                        ContractManage::getLastFollowDate, updateDTO.getLastFollowDate())
                .set(updateDTO.getNextFollowDate() != null,
                        ContractManage::getNextFollowDate, updateDTO.getNextFollowDate())
        ;
        return update(updateWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateFollow(FollowRecordPlanSaveDTO saveDTO) {
        ContractManage contractManage = getById(saveDTO.getContractManageId());
        if (contractManage == null) {
            throw BizException.wrap(VenueBookingExceptionCode.CONTRACT_DOES_NOT_EXIST);
        }
        LocalDateTime lastFollowDate = FollowRecordService.getLastFollowDate(saveDTO.getFollowDateTime(), contractManage.getLastFollowDate());
        LocalDateTime nextFollowDate = contractManage.getNextFollowDate();
        if (saveDTO.getFollowPlanSaveDTO() != null) {
            nextFollowDate = FollowPlanService.getNextFollowDate(saveDTO.getFollowPlanSaveDTO().getPlanFollowDate(), contractManage.getNextFollowDate());
        }
        // 更新合同
        ContractManageDTO updateDTO = new ContractManageDTO();
        updateDTO.setId(saveDTO.getContractManageId());
        updateDTO.setContractName(saveDTO.getContractManageName());
        updateDTO.setLastFollowDate(lastFollowDate);
        if (saveDTO.getFollowPlanSaveDTO() != null) {
            updateDTO.setNextFollowDate(nextFollowDate);
        }
        return updateFollow(updateDTO);
    }

    /**
     * @param invoiceCallbackDTO
     * @return void
     * @Author clm
     * @Description 开票回填
     * @Date 14:42 2022/7/14
     * @Param [invoiceCallbackDTO]
     */
    @Override
    public void updateForInvoice(InvoiceCallbackDTO invoiceCallbackDTO) {
        ContractManage contract = getById(invoiceCallbackDTO.getBusinessId());
        contract.setInvoicedAmount(invoiceCallbackDTO.getInvoicedAmount());
        updateById(contract);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeContractByIds(List<Long> ids) {
        log.info("removeContractByIds - 删除合同, ids={}", ids);
        // 存在认款记录不能删除
        for (Long id : ids) {
            if (!venueBookingContractService.isCollected(id)) {
                throw BizException.wrap(VenueBookingExceptionCode.CONTRACT_HAS_COLLECT);
            }
        }
        // 移除合同
        List<ContractManage> list = listByIds(ids);
        removeByIdsOfLogic(list);
        // 移除支付系统合同
        for (Long id : ids) {
            venueBookingContractService.delete(id);
        }
        // 取消支付系统订单
        for (ContractManage contractManage: list) {
            venueBookingTransactionOrderService.cancel(contractManage.getContractNumber());
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeContractByExhibitionId(Long exhibitionId) {
        log.info("removeContractByExhibitionId - 展会ID删除合同, exhibitionId={}", exhibitionId);
        List<ContractManage> contractManages = listContractManageByExhibitionId(exhibitionId);
        if (contractManages == null || contractManages.isEmpty()) {
            return false;
        }
        List<Long> ids = contractManages.stream()
                .map(SuperEntity::getId)
                .collect(Collectors.toList());
        return removeContractByIds(ids);
    }

    @Override
    public boolean saveTransactionOrder(ContractManageDTO contractManageDTO) {
        log.info("saveTransactionOrder - 保存支付系统交易订单, contractManageDTO={}", contractManageDTO);
        TransactionOrderDTO transactionOrderDTO = new TransactionOrderDTO();
        transactionOrderDTO.setExhibitionId(contractManageDTO.getExhibitionId());
        transactionOrderDTO.setExhibitionName(contractManageDTO.getExhibitionName());
        transactionOrderDTO.setOrderType(TransactionOrderOrderTypeEnum.CONTRACT);
        transactionOrderDTO.setOrderName(contractManageDTO.getContractName());
        transactionOrderDTO.setOrderNum(contractManageDTO.getContractNumber());
        transactionOrderDTO.setOrderAmount(contractManageDTO.getContractAmount());
        transactionOrderDTO.setOrderTime(contractManageDTO.getCreateTime());
        transactionOrderDTO.setOrderStatus(TransactionOrderOrderStatusEnum.UNPAY);
        transactionOrderDTO.setPayer(contractManageDTO.getCutomerName());
        transactionOrderDTO.setPayee(contractManageDTO.getPayee());
        transactionOrderDTO.setSystemCode(MenuAffiliationEnum.P_VENUE.getCode());
        List<PayApiPaymentPlanDTO> paymentPlanList = contractManageDTO.getPaymentPlanDTOList();
        if (paymentPlanList == null || paymentPlanList.isEmpty()) {
            transactionOrderDTO.setPaymentPeriods(1);
        } else {
            // 添加付款计划
            transactionOrderDTO.setPaymentPeriods(paymentPlanList.size());
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            List<OrderPaymentPlan> orderPaymentPlanList = paymentPlanList
                    .stream()
                    .map(payApiPaymentPlanDTO -> {
                        OrderPaymentPlan orderPaymentPlan = new OrderPaymentPlan();
                        LocalDateTime planPayTime = payApiPaymentPlanDTO.getPlanPayTime();
                        if (planPayTime != null) {
                            String fkrq = planPayTime.format(dateTimeFormatter);
                            orderPaymentPlan.setFkrq(fkrq);
                        }
                        orderPaymentPlan.setFkje(payApiPaymentPlanDTO.getPlanPayAmount());
                        return orderPaymentPlan;
                    })
                    .collect(Collectors.toList());
            transactionOrderDTO.setPaymentPlan(orderPaymentPlanList);
        }
        return venueBookingTransactionOrderService.save(transactionOrderDTO);
    }

    @Override
    public boolean saveTransactionOrder(String contractNumber) {
        log.info("saveTransactionOrder - 保存支付系统交易订单, contractNumber={}", contractNumber);
        ContractManage contractManage = getByContractNumber(contractNumber);
        if (contractManage == null) {
            throw BizException.wrap(VenueBookingExceptionCode.CONTRACT_DOES_NOT_EXIST);
        }
        ContractManageDTO contractManageDTO = BeanUtil.toBean(contractManage, ContractManageDTO.class);
        List<PaymentPlanVO> paymentPlanVOs = venueBookingPaymentPlanService.queryByContractId(contractManage.getId());
        List<PayApiPaymentPlanDTO> payApiPaymentPlanDTOList = paymentPlanVOs
                .stream()
                .map(paymentPlanVO -> BeanUtil.toBean(paymentPlanVO, PayApiPaymentPlanDTO.class))
                .collect(Collectors.toList());
        contractManageDTO.setPaymentPlanDTOList(payApiPaymentPlanDTOList);
        contractManageDTO.setPayee(venueBookingPayee);
        return saveTransactionOrder(contractManageDTO);
    }

    @Override
    public boolean updateTransactionOrder(ContractManageDTO contractManageDTO) {
        log.info("updateTransactionOrder - 更新支付系统交易订单, contractManageDTO={}", contractManageDTO);
        TransactionOrderDTO transactionOrderDTO = new TransactionOrderDTO();
        transactionOrderDTO.setOrderNum(contractManageDTO.getContractNumber());
        List<PayApiPaymentPlanDTO> paymentPlanList = contractManageDTO.getPaymentPlanDTOList();
        if (paymentPlanList == null || paymentPlanList.isEmpty()) {
            transactionOrderDTO.setPaymentPeriods(1);
        } else {
            // 添加付款计划
            transactionOrderDTO.setPaymentPeriods(paymentPlanList.size());
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            List<OrderPaymentPlan> orderPaymentPlanList = paymentPlanList
                    .stream()
                    .map(payApiPaymentPlanDTO -> {
                        OrderPaymentPlan orderPaymentPlan = new OrderPaymentPlan();
                        LocalDateTime planPayTime = payApiPaymentPlanDTO.getPlanPayTime();
                        if (planPayTime != null) {
                            String fkrq = planPayTime.format(dateTimeFormatter);
                            orderPaymentPlan.setFkrq(fkrq);
                        }
                        orderPaymentPlan.setFkje(payApiPaymentPlanDTO.getPlanPayAmount());
                        return orderPaymentPlan;
                    })
                    .collect(Collectors.toList());
            transactionOrderDTO.setPaymentPlan(orderPaymentPlanList);
        }
        return venueBookingTransactionOrderService.update(transactionOrderDTO);
    }

    @Override
    public boolean sendInnerMsgWhenPayPlanDeadLine(ContractManageVO contractManageVO) {
        log.info("sendInnerMsgWhenPayPlanDeadLine- 合同付款计划到期支付站内信提醒, contractManageVO={}", contractManageVO);
        List<PayApiPaymentPlanVO> paymentPlanList = contractManageVO.getPaymentPlanList();
        if (paymentPlanList == null || paymentPlanList.isEmpty()) {
            log.info("sendInnerMsgWhenPayPlanDeadLine - 付款计划缺失，无法发送");
            return false;
        }
        for (PayApiPaymentPlanVO payApiPaymentPlanVO : paymentPlanList) {
            String content = String.format(BusinessInnerMsgTemplate.CONTRACT_PAYMENT_PLAN_DEADLINE_TEMPLATE, contractManageVO.getContractName(), payApiPaymentPlanVO.getStagesTag());
            sendMsgUtil.sendInnerMsg(content, StaticContextUtil.getProjectSystemScene());
        }
        return true;
    }

    @Override
    public void asyncSendInnerMsgWhenPayPlanDeadLine(ContractManageVO contractManageVO) {
        threadPoolTaskExecutor.execute(() -> sendInnerMsgWhenPayPlanDeadLine(contractManageVO));
    }

    private QueryWrapper<ContractManage> createContractManageQueryWrapper(ContractManageQuery query) {
        QueryWrapper<ContractManage> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .eq("conm.is_deleted", BusinessConstant.DELETE_NO)
                .eq(query.getIsEnabled() != null, "conm.is_enabled", query.getIsEnabled())
                .and(StrUtil.isNotBlank(query.getKeyword()), contractManageQueryWrapper -> contractManageQueryWrapper
                        .like("conm.contract_name", query.getKeyword())
                        .or()
                        .like("ex.exhibition_name", query.getKeyword()))
                .eq(StringUtils.isNotBlank(query.getContractPayState()),
                        "conm.contract_pay_state", query.getContractPayState())
                .between(StrUtil.isNotBlank(query.getStartSigningTime())
                                && StrUtil.isNotBlank(query.getEndSigningTime()),
                        "conm.signing_time", query.getStartSigningTime(), query.getEndSigningTime())
                .eq(query.getExhibitionId() != null, "conm.exhibition_id", query.getExhibitionId())
                .eq(query.getBelongTo() != null, "conm.belong_to", query.getBelongTo())
                .like(StrUtil.isNotBlank(query.getBelongToName()), "conm.belong_to_name", query.getBelongToName())
        ;
        return queryWrapper;
    }

    /**
     * 模版套打，生成合同文件
     *
     * @param contractManage 合同
     */
    private void processContractTemplate(ContractManage contractManage) {
        // 模板套打
        TemplateManage template = templateManageService.getById(contractManage.getContractTemplateId());
        if(template == null){
            throw BizException.wrap(VenueBookingExceptionCode.CONTRACTNOT_TEMPLATE);
        }
        Map<String,Object> params = new HashMap<>(16);
        params.put("contractNumber", contractManage.getContractNumber());
        params.put("contractName", contractManage.getContractName());
        params.put("exhibitionName", contractManage.getExhibitionName());
        params.put("contractAmount", contractManage.getContractAmount());
        params.put("cutomerName", contractManage.getCutomerName());
        params.put("customerLegal", contractManage.getCustomerLegal());
        params.put("customerAddress", contractManage.getCustomerAddress());
        params.put("customerUscc", contractManage.getCustomerUscc());
        params.put("address", contractManage.getCustomerAddress());
        params.put("uscc", contractManage.getCustomerUscc());
        params.put("sponsorName", contractManage.getSponsorName());
        params.put("undertakerName", contractManage.getUndertakerName());
        params.put("startTime", contractManage.getStartTime());
        params.put("endTime", contractManage.getEndTime());
        params.put("signingTime", contractManage.getSigningTime());
        params.put("signatoryName", contractManage.getSignatoryName());
        params.put("spaceBond", contractManage.getSpaceBond());
        params.put("serviceBond", contractManage.getServiceBond());

        File file;
        // 合同数据套打入合同模版，生成合同文件
        try {
            file = WordUtil.createFileByTemplate(template.getTemUrl(), params, contractManage.getContractName());
        } catch (IOException e) {
            log.error("网络错误，生成合同文件失败", e);
            throw BizException.wrap(1001, "网络错误，生成合同文件失败");
        } catch (XDocReportException e) {
            log.error("加载模版错误，生成合同文件失败", e);
            throw BizException.wrap(1001, "加载模版错误，生成合同文件失败");
        }
        // 生成的合同文件上传到文件服务器
        try (FileInputStream fileInputStream = new FileInputStream(file)) {
            MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(), ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
            R<FileVO> fileResult = fileServiceFeign.serviceUploadFile(multipartFile, "pvenue", "true");
            if (!fileResult.getIsSuccess()) {
                throw BizException.wrap(1005, "调用文件服务上传失败");
            }
            FileVO fileVO = fileResult.getData();
            // 合同url传入合同
            contractManage.setContractUrl(fileVO.getPath());
        } catch (FileNotFoundException e) {
            log.error("合同文件不存在，上传失败", e);
            throw BizException.wrap(1002, "合同文件不存在，上传失败");
        } catch (IOException e) {
            log.error("网络错误，合同文件上传失败", e);
            throw BizException.wrap(1003, "网络错误，合同文件上传失败");
        } catch (BizException e) {
            log.error(e.getMessage(), e);
            throw e;
        }
        catch (Exception e) {
            log.error("合同文件上传失败", e);
            throw BizException.wrap(1004, "合同文件上传失败");
        }
    }
}
